《学修項目》
《キーワード》
データの種類,数と文字列,ビットとバイト,数の表現方法,構造化されたデータ,非構造化データ,配列とデータ,線形探索,木構造,二分探索,テキストデータ,画像データ,音声データ,動画データ
データの処理を事後的にやりやすくするため,ある特定の目的のために収集されたデータは,あらかじめデータの並べ方を厳格に定めている. このようなデータのタイプを構造化データ(structured data)と呼ぶ.
わかりやすい例は,表計算用のデータである. データ全体を目的毎のテーブルに分割し,さらにテーブルに記録される項目をフィールドとして定義し,そして各データはレコードという単位で記録・管理する. このように構造化されたデータがデータベース(database)である.
計算機に構造化データの処理作業を指示するときには,データの並べ方のルールに従わなくてはならない.逆にルールに従っていれば,データの並び方の構造を上手に利用した,高速かつ大規模なデータに対する検索や処理が可能になる.
データの並べ方に「ルール」がないデータのことを総称して非構造化データ(unstructured data)と呼ぶ. 文書,画像,音声,音楽,動画などは,それぞれ格納されるデータの「単位」は同一であっても,それが表現される形式はバラバラである.
例えば文書(テキストデータ)については何の言語で書かれたどの文字コードで表現されたものかが特定されていたとしても,それは非構造的である. 日本語であれば形態素解析を行わなければ単語に分割されないし,単語間の連携は文法の解析を行わなければならない.文法の解析が終われば,構文木として初めて構造化されたデータとして手に入ることになる.
非構造化データを取り扱うためには,メタデータ(対象データに付随して説明する何らかの情報)が重要である.
例えば画像データそれ自体は非構造化データである. 元の画像情報は,たとえばラスタースキャンの方向でピクセル(pixel)と呼ぶ最小単位で構成されている. 各ピクセルは色や輝度を有していることが普通であるから,その情報を各色空間でエンコードしてデジタル化する.
ここで対象とする画像データを解析,あるいは「その画像は,何の画像なのか」を認識することを考える. 機械的な(計算した結果としての)画像認識とは,画像中の物体(オブジェクト)は何なのかを「言葉」で答える問題である.これは物体認識と呼ばれ,
に大別される.
交通標識のように,標識マークの数が限られている状況を考えてみよう. 特定物体認識とは,画像から何の交通標識か,つまり画像に対応した言葉を認識する課題である. 各国で交通標識は法令で定められており,矢印の形や色,赤い丸の中に数字が入っているなど形式が統一されているから,処理能力が低いエッジ向け推論フレームワークなどでも実装が容易である.
一般物体認識では,ありとあらゆる言葉(対象物,意味)に対応した画像を取り扱う. 当然,一般物体認識のほうが格段に難しい.一般物体認識でも,画像の中に1つ物体が入っている問題と,複数物体が入っている問題とはまた格段に難易度が異なる.
一般物体認識の問題は,認識結果を最終的にどのような形式で出力するかによって,(a)分類(b)物体検出(c)セマンティックセグメンテーションの3つに大別され,この順に難易度は上がっていく(参考サイト).
(a) 画像の分類とは,画像に写っている物体のカテゴリ(クラスと呼ばれる)を認識結果として出力する問題である.
(b) 画像から物体を認識するためには,画像の中から物体の境界を切り出す,セグメンテーション(segmentation)という作業が必要になる.一般物体認識で実現されるAI機能として,物体らしき部分を特定する作業がセグメンテーションである.
(c)セグメンテーションの後,囲われたものの名前(一般に理解可能な名称)を特定する.特定した物体の様相(例:車輪が2つ付いていてフレームで結合されたものとしてのモーターサイクル)に応じた領域を推定する.
Pythonによる実装については,クラウド環境であれば Google colab.が即時使える. ローカルPC上の環境であれば,Windows,macOS,Linuxなどで各々別途インストール作業が必要となる. 詳細は こちらの参考書籍付属の公開資料を参照されたい[3]
Pythonでは整数と小数は別のデータ型である. データの型は,組み込み関数typeで調べる.
type(2) # 整数
int
type(2.0) # 小数
float
整数同士の演算は整数となり,整数と小数の演算結果は小数になる.
123 + 456 # 整数同士の加算
1024 / 256 # 除算の結果は float となる
4.0
10 // 3 # 結果を整数で得る除算
3
65535 % 16 # 16の剰余を取る
15
binary_num = 0b1010 # Base2
octal_num = 0o2040 # Base8
hexadecimal_num = 0xFFFF # Base16
print(binary_num)
print(octal_num)
print(hexadecimal_num)
10 1056 65535
binary_num + octal_num + hexadecimal_num # 数値はすべて整数型intであるので,普通に演算可
66601
print(0b1111_1111_1111_1111 == 0xFFFF) # 2進数はアンダースコア _ で区切ることができる. 比較演算子 == を使って 2つの数値が等しいか否かを調べる
True
bin(123) # 組込関数binは,整数の2進数表現を返す(結果は文字列型)
'0b1111011'
bin(123*2) # 2進数はBase2であるので,2倍すればビットが左シフトする
123 << 1 # ビットシフト演算子を使っても同じ結果である
246
import sys
print(sys.float_info.max) # 浮動小数点で表現できる最大値
print(sys.float_info.min) # 浮動小数点で表現できる最小値
1.7976931348623157e+308 2.2250738585072014e-308
print('{:.3e}'.format(12345)) ##小数点以下の桁数を指定して指数にしたい場合
1.234e+04
数学の定数などは mathライブラリをインポートして使う. 円周率は math.pi で定義されている.
float型をそのまま print で表示すると桁が多すぎるので,適宜 format指示子を用いて表示を制御する.
import math # mathライブラリをインポート
diag = 2.0 # 変数 diag に半径を代入
area = diag**2 * math.pi # 円の面積を求める
print('{0:.4f}'.format(area)) # 小数点以下4桁でfloat表示する
12.5664
数式処理ライブラリ 『SymPy』 を使って,初等関数に関する種々の操作・演算・微分積分などを行ってみる.
!pip install sympy
Requirement already satisfied: sympy in /usr/local/lib/python3.10/site-packages (1.11.1) Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/site-packages (from sympy) (1.2.1)
import sympy as sym # SymPy ライブラリ をインポート
from sympy.plotting import plot3d # Mathplot経由でSymPyの結果を描画する設定
sym.init_printing(use_unicode=True)
%matplotlib inline
a, b, c, x, y = sym.symbols("a b c x y") # 変数 a,b,c,x,y を定義しておく
expr = (x+y)**6 # 数式を定義
sym.expand(expr) # 多項式を展開する
plot3d(expr, (x, -10, 10), (y, -10, 10)) # 指定した区間でグラフを描画
<sympy.plotting.plot.Plot at 0x153f42c80>
sym.simplify(1/(x-1)**2+1/((x-1)*(x-2))) # 式を簡単化する
sym.solve(a*x**2+b*x+c, x) # sympy.solve(eq, var)を使って,2次方程式を求解する
1変数実関数の微分積分
def f(x): return sym.sin(4*x)**2 # 関数 f(x) を定義する (三角関数の例)
#def f(x): return -sym.log(1-x)/x # 関数 f(x) を定義する (対数関数の例)
sym.simplify(f(x))
sym.diff(f(x), x, 1) # 変数xで f(x)を微分した結果を得る
sym.integrate(f(x), x) # 変数xで f(x)を積分した結果を得る
my_array = [10, 20, 30, 40, 50] # 5つの整数が格納された配列データ(一番最初のインデックスはゼロ)
my_array
my_array[2] # インデックス#2 (3番目) の要素を参照する
my_array.insert(2, 100) # インデックス#2の位置に 100 を挿入する
my_array
my_array.append(300) # 一番後ろに 300 を挿入する
my_array
# 2次元配列を定義する.
# 外側の角カッコが外側(1次元目)の配列で,中側の角カッコが内側(2次元目)の配列になる.
my_array2 = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
]
my_array2
# この場合,外側の配列の要素数は3つで,中側の配列の要素数もそれぞれ3つずつになる.
# この状態の二次元配列を行列(正方行列)と表現することもある
my_array2[1][2] # 要素(1,2)を指定する(結果はスカラー)
my_array2[1] # 2行目全体を指定する(結果は横ベクトル)
my_array2.append([9, 10, 11]) # 4行目を追加する(サイズが合っている必要がある)
my_array2
import numpy as np # numpyライブラリをインポート
# 2次元配列を定義する(標準ライブラリと同じ)
np.array2 = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
]
np.array2
id3_array = np.eye(3) # 3x3の単位行列を生成
print(id3_array)
[[1. ... 0.] ... [0. ... 1.]]
np.array2 * id3_array # そのまま * を使うと アダマール積 になる
array([[0., ..., 0.],
...,
[0., ..., 8.]])
np.dot(np.array2,id3_array) # 通常の内積は dot を使う
array([[0., ..., 2.],
...,
[6., ..., 8.]])
# numpy高速計算の例(おまけ)
huge_array = np.random.randn(10000000) # 1,000万サイズの1次元配列(乱数を用いて生成)
huge_array
array([ 1.23951637, ..., -0.40586375])
%timeit np.sum(huge_array) # numpyのsumメソッド(高速)を使って,巨大なリストの和を求める計算の時間計測
1.95 ms ± 21.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit sum(huge_array) # 標準ライブラリのsum関数(低速)を使って,巨大なリストの和を求める計算の時間計測(遅い)
695 ms ± 9.54 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
木構造の代表的な表現方法は (1)隣接リスト,(2)隣接行列 の2つです. いずれも多重配列を用いて実現することができます[1].
Pythonでゼロから木構造を実装するには,最初に単一のノードを表す Node クラスから作成する必要がある. しかもクラスにはノードの追加や削除,トラバース(横断検索のイテレータ)などをメソッドとして実装する必要があり非常に面倒であるし,バグの温床にもなりかねない.
したがって,Pythonで木構造を簡単かつ信頼性高く実装するため, anytreeライブラリを使用する.
# 最初に anytreeライブラリ を pipでインストールする
!pip install anytree --user
# 実行後,最後の行で Successfully installed anytree-*.*.* (*.*.*はバージョン番号) と表示されればOK
Requirement already satisfied: anytree in /usr/local/lib/python3.10/site-packages (2.8.0) Requirement already satisfied: six>=1.9.0 in /usr/local/lib/python3.10/site-packages (from anytree) (1.16.0)
from anytree import Node, RenderTree, AsciiStyle # anytreeから,必要な機能をインポート
# 各ノードを構成していく.rootノードはそのまま,childノードはparentノードを指定する.
f = Node("f") # rootノードは "f" で,これが木構造のインスタンス名となる
b = Node("b", parent=f)
a = Node("a", parent=b)
d = Node("d", parent=b)
c = Node("c", parent=d)
e = Node("e", parent=d)
g = Node("g", parent=f)
i = Node("i", parent=g)
h = Node("h", parent=i)
print(RenderTree(f, style=AsciiStyle()).by_attr()) # RenderTreeを使って木構造をテキストベースで可視化
f
|-- b
| |-- a
| +-- d
| |-- c
| +-- e
+-- g
+-- i
+-- h
※以下の例は,実行環境に Graphviz (dotコマンド) がインストールされている必要がある.
from anytree.exporter import DotExporter # GraphVizを使って木構造を可視化する
DotExporter(f).to_picture("tree.png") # カレントディレクトリに tree.png という名前の画像ファイルとして保存する
from IPython.display import Image # Ipython.display モジュールを使って、対話型ノートブックの中に追加のファイルを表示する
Image('tree.png') # カレントディレクトリの tree.png 画像ファイルを読み込んでノートブック上に表示する
グラフは,ネットワーク状にデータを格納するデータ構造です. 交通網やSNSの接続関係などもグラフで表現することができます.
Pythonでグラフ構造を簡単かつ信頼性高く実装するため, NetworkXライブラリを使用する[3].
# 最初に NetworkXライブラリ を pipでインストールする
!pip install networkx
# 実行後,最後の行で Successfully installed network-*.* (*.*はバージョン番号) と表示されればOK
Collecting networkx
Downloading networkx-2.8.6-py3-none-any.whl (2.0 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.0/2.0 MB 257.6 kB/s eta 0:00:00[36m0:00:01m eta 0:00:01
Installing collected packages: networkx
Successfully installed networkx-2.8.6
%matplotlib inline
import matplotlib.pyplot as plt
import networkx as nx # NetworkX ライブラリをインポート
# 簡単なグラフを生成してみる
graph = nx.Graph() # 無向グラフ
graph.add_edge(5,6) # ノード間の接続関係を定義
graph.add_edge(6,7)
graph.add_edge(5,8)
graph.add_edge(5,7)
nx.draw_networkx(graph, node_color='darkseagreen') # mathplot経由でグラフを可視化
graph2 = nx.Graph() # 無向グラフ
graph2.add_edges_from([("A", "B"), ("B", "C"), ("B", "F"),("C", "D"), ("C", "E"), ("C", "F"), ("B", "F")]) # 接続関係のsetを利用してまとめて定義
nx.draw_networkx(graph2, node_color='darkseagreen') # mathplot経由でグラフを可視化
グラフをランダム生成して表示してみる
import random
# グラフ生成する関数を定義する
def generate_graph(n, m):
""" n個の頂点とm個の辺をもつグラフを作る """
graph_data = [[0] * n for i in range(n)]
# 同じ辺が同一視されるように set を用意する
edge_set = set()
while len(edge_set) < m:
i, j = random.sample(range(n),2)
if i > j: i, j = i, i
edge_set.add((i,j))
graph_data[i][j] = graph_data[j][i] = i
return graph_data, edge_set
# 試しに,16頂点に対して20の辺を生成したランダムグラフを生成してみる
random.seed(6)
node_num = 16
edge_num = 20
my_graph, edge_set = generate_graph(node_num, edge_num)
edge_set # 接続関係のsetを表示
my_graph # 隣接行列表現
graph3 = nx.Graph() # 無向グラフ
graph3.add_edges_from(edge_set) # 接続関係のsetを利用してまとめて定義
pos3 = nx.spring_layout(graph3)
nx.draw_networkx(graph3, pos3, node_color='darkseagreen') # mathplot経由でグラフを可視化
自然言語で書かれた文(一般的に句点で区切られる単位)や文章(複数の文からなり,段落など,何らかの一つのまとまりをなすもの),文書(レポートや新聞記事などそれ一つで完結しているもの)を総称してテキストと呼ぶ[4].
テキスト分析は,一般に自然言語処理(natural language processing)と呼ばれる技術に含まれる.
自然言語処理は大きく2つの研究領域にわけることができる. 1つは基礎技術であり,何らかのアプリケーションそのものというよりは,何らかのアプリケーションを開発する際に要素技術として利用される技術群である. 基礎技術には,形態素解析,構文解析,意味解析,文脈解析などがある.
もう1つは応用技術であり,具体的なアプリケーションそのものである. 応用技術には情報抽出や情報検索,評判分析,機械翻訳などが含まれる.
形態素解析は,文を形態素と呼ばれる単位に分割する処理である.ここでいう文とは,通常日本語においては句点で区切られる単位である.
形態素解析は,大きく2つの作業にわけられる.まず,形態素辞書を利用して,文からラティスと呼ばれるデータ構造を作成する作業を行う.次に,そのラティス上で,最もらしい形態素列を探索する.
日本語の文は分かち書きされていないため,解析のためにはまず文頭から一文字ずつ形態素辞書をひきながら,辞書に記述されている単語を探していく.その結果,形態素ラティスと呼ばれるグラフが構築される.
複数の解析候補が存在する場合,どの解析候補が最も適切かを判断するため,隣接する形態素の確率を考える. 確率は文書カテゴリごとに様相が異なるので,単語境界の情報が付与されたコーパスに基づいて計算する(ビタビ・アルゴリズムを使用する).
構文解析とは文の構文構造を明らかにする処理である.日本語では,伝統的に係り受けと呼ばれる構造を解析する係り受け解析として研究が行われてきた.文節の境界を明らかにすることを,チャンキング(chunking)と呼ぶことがある.これは,文節の境界の同定を,文を文節という塊(chunk)に分ける処理と考えることができるためである.
文節の境界を明らかにする手法としては,規則に基づく手法と機械学習に基づく手法がある.
規則に基づく手法では,形態素解析の結果に,人手で作成した規則を適用し,文節の境界を定める.
機械学習に基づく手法としては,あらかじめ文節区切りが付与されたコーパスから,文節の境界を機械学習に基づいて推定する.よく用いられる手法として,BIO法がある.
!pip install mecab-python3 #MeCabライブラリをインストール
!pip install unidic-lite #辞書のインストール(国立国語研究所 https://clrd.ninjal.ac.jp/unidic/about_unidic.html)
Collecting mecab-python3
Downloading mecab_python3-1.0.5-cp310-cp310-macosx_10_15_x86_64.whl (282 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 282.8/282.8 kB 2.6 MB/s eta 0:00:00[36m0:00:01[36m0:00:01
Installing collected packages: mecab-python3
Successfully installed mecab-python3-1.0.5
Collecting unidic-lite
Downloading unidic-lite-1.0.8.tar.gz (47.4 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 47.4/47.4 MB 2.3 MB/s eta 0:00:00m eta 0:00:01[36m0:00:01
Preparing metadata (setup.py) ... done
Building wheels for collected packages: unidic-lite
Building wheel for unidic-lite (setup.py) ... done
Created wheel for unidic-lite: filename=unidic_lite-1.0.8-py3-none-any.whl size=47658836 sha256=550b98f957732b679b92391fb1d744858b0904c21f0b8a1ae39230a70127c0fa
Stored in directory: /Users/wasaki/Library/Caches/pip/wheels/89/e8/68/f9ac36b8cc6c8b3c96888cd57434abed96595d444f42243853
Successfully built unidic-lite
Installing collected packages: unidic-lite
Successfully installed unidic-lite-1.0.8
簡単な日本語の文章を MeCab (wakati) 解析によって分かち書きしてみる
import MeCab
wakati = MeCab.Tagger("-Owakati")
wakati.parse("彼は森の中を歩いた.").split()
#wakati.parse("確率の計算には,形態素解析と同様に条件付き確率場などのモデルを利用することができる.").split()
#wakati.parse("とうきょうとっきょきょかきょく").split()
#wakati.parse("すもももももももものうち").split()
['彼', 'は', '森', 'の', '中', 'を', '歩い', 'た', '.']
tagger = MeCab.Tagger()
print(tagger.parse("彼は森の中を歩いた."))
#print(tagger.parse("すもももももももものうち"))
彼 カレ カレ 彼 代名詞 1 は ワ ハ は 助詞-係助詞 森 モリ モリ 森 名詞-普通名詞-一般 0 の ノ ノ の 助詞-格助詞 中 ナカ ナカ 中 名詞-普通名詞-副詞可能 1 を オ ヲ を 助詞-格助詞 歩い アルイ アルク 歩く 動詞-一般 五段-カ行 連用形-イ音便 2 た タ タ た 助動詞 助動詞-タ 終止形-一般 . . 補助記号-句点 EOS
単語を出現数順に抽出してみる.
#Wikipedia「形態素解析」の序文を切り出してきたものを text に代入
text = '''形態素解析とは、文法的な情報の注記の無い自然言語のテキストデータ(文)から、\
対象言語の文法や、辞書と呼ばれる単語の品詞等の情報にもとづき、形態素(おおまかにいえば、\
言語で意味を持つ最小単位)の列に分割し、それぞれの形態素の品詞等を判別する作業である。\
自然言語処理の分野における主要なテーマのひとつであり、機械翻訳やかな漢字変換など応用も多い\
(もちろん、かな漢字変換の場合は入力が通常の文と異なり全てひらがなであり、その先に続く文章も\
その時点では存在しないなどの理由で、内容は機械翻訳の場合とは異なったものになる)。\
もっぱら言語学的な観点を主として言語学で研究されている文法にもとづく解析もあれば、\
コンピュータ上の自然言語処理としてコンピュータでの扱いやすさに主眼を置いた解析もある。\
以下は後者のためのツールを用いた例で、「お待ちしております」という文を形態素解析した例である\
(「茶筌」を使用した)。'''
# MeCabで形態素を parseToNodeで抽出してきた後, 品詞が[名詞]に合致するものだけを検索し,
# それを noun_count[word]ハッシュに格納していく.すでに出現している名詞であれば出現数を+1する.
mecabTagger = MeCab.Tagger()
noun_count = {}
node = mecabTagger.parseToNode(text)
while node:
word = node.surface
hinshi = node.feature.split(",")[0]
if word in noun_count.keys() and hinshi == "名詞":
noun_freq = noun_count[word]
noun_count[word] = noun_freq + 1
elif hinshi == "名詞":
noun_count[word] = 1
else:
pass
node = node.next
# 最後に出現順に降順ソートしたものを生成する.
noun_count = sorted(noun_count.items(), key=lambda x:x[1], reverse=True)
print(noun_count)
[('言語', 7), ('形態', 4), ('解析', 4), ('文法', 3), ('自然', 3), ('文', 3), ('情報', 2), ('品詞', 2), ('処理', 2), ('機械', 2), ('翻訳', 2), ('かな', 2), ('漢字', 2), ('変換', 2), ('場合', 2), ('コンピュータ', 2), ('例', 2), ('注記', 1), ('テキスト', 1), ('データ', 1), ('対象', 1), ('辞書', 1), ('単語', 1), ('意味', 1), ('最小', 1), ('単位', 1), ('列', 1), ('分割', 1), ('それぞれ', 1), ('判別', 1), ('作業', 1), ('分野', 1), ('テーマ', 1), ('ひと', 1), ('応用', 1), ('入力', 1), ('通常', 1), ('全て', 1), ('ひらがな', 1), ('先', 1), ('文章', 1), ('時点', 1), ('存在', 1), ('理由', 1), ('内容', 1), ('もの', 1), ('観点', 1), ('主', 1), ('研究', 1), ('主眼', 1), ('以下', 1), ('後者', 1), ('ため', 1), ('ツール', 1), ('茶筌', 1), ('使用', 1)]
以下は Python自然言語処理101本ノックその2 の最初のコードの抜粋である(Google Colab環境のみ).
# (引用元での動作)確認日時(2020/7/19)
!apt install -y curl file git libmecab-dev make mecab mecab-ipadic-utf8 swig xz-utils
!pip install mecab-python3
import os
filename_crfpp = 'crfpp.tar.gz'
!wget "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7QVR6VXJ5dWExSTQ" \
-O $filename_crfpp
!tar zxvf $filename_crfpp
%cd CRF++-0.58
!./configure
!make
!make install
%cd ..
os.environ['LD_LIBRARY_PATH'] += ':/usr/local/lib'
FILE_ID = "0B4y35FiV1wh7SDd1Q1dUQkZQaUU"
FILE_NAME = "cabocha.tar.bz2"
!wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=\
$(wget --quiet --save-cookies /tmp/cookies.txt \
--keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=$FILE_ID' -O- \
| sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=$FILE_ID" -O $FILE_NAME && rm -rf /tmp/cookies.txt
!tar -xvf cabocha.tar.bz2
%cd cabocha-0.69
!./configure --with-mecab-config=`which mecab-config` --with-charset=UTF8
!make
!make check
!make install
%cd ..
%cd cabocha-0.69/python
!python setup.py build_ext
!python setup.py install
!ldconfig
%cd ../
%cd cabocha-0.69
!make
!make check
!make install
%cd ../
# インストールチェック
!mecab --version
!cabocha --version
# CaboChaを使って構文解析を実行する
import CaboCha
cbc = CaboCha.Parser()
target = "彼は森の中を歩いた"
#target = "私は永遠に駆け出しエンジニアスピリットで学び続けるんだ。そしてプロになるためにまず100本ノックしよう。"
# ラティス形式で出力
print(cbc.parse(target).toString(CaboCha.FORMAT_LATTICE))
# 構文ツリー形式で出力
print(cbc.parse(target).toString(CaboCha.FORMAT_TREE))
ここでは画像解析の最初の一歩として,プログラミング言語であるPythonと画像処理ライブラリであるOpenCVを使って,ファイルからの画像の読み込み,グレイスケール変換,HSVヒストグラムの確認,ガンマ補正,そして処理後の画像ファイルの書き出しを体験してみる[4].
以下では,基本的に Google colab.環境下でのPythonとOpenCVライブラリを前提として実装されている. また,サンプル画像のダウンロードには wgetコマンドを使用しているが,これはGoogle colab. あるいは Linux環境などで標準的に入っているが,Windows版ローカル実行の jupyter notebookでは別途インストールと設定作業が必要なので注意されたい.
入力画像は,各自がスマートフォンで撮影した画像など,任意の画像を“sample.jpg”という名前でソースコードと同じフォルダ(Google colab.の場合は,各セッションに割当てられる一時作業領域)に保存してあるものとして説明する.
!pip install opencv-python
Collecting opencv-python
Downloading opencv_python-4.6.0.66-cp36-abi3-macosx_10_15_x86_64.whl (46.4 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 46.4/46.4 MB 1.5 MB/s eta 0:00:00m eta 0:00:01[36m0:00:01
Requirement already satisfied: numpy>=1.14.5 in /usr/local/lib/python3.10/site-packages (from opencv-python) (1.23.2)
Installing collected packages: opencv-python
Successfully installed opencv-python-4.6.0.66
%matplotlib inline
import cv2
from matplotlib import pyplot as plt
import numpy as np
#import cv2 # OpenCV2ライブラリのインポート
#import matplotlib.pyplot as plt
print(cv2.__version__) # バージョン表示
4.6.0
# JPEG画像データを カレントディレクトリ直下のフォルダ(一時作業領域)へダウンロードする.
!wget -nc https://raw.githubusercontent.com/MDASH-shinshu/MDASH-T-DE/main/2/resources/sample.jpg
# wgetしなくても,Google colab.の左メニュー [ファイル] アイコンをクリックして,ブラウザへファイルをドラッグ&ドロップしても可
# JPEG画像ファイル(例えば sample.jpg) がダウンロード・配置できたことを確認する
!ls -al ./
--2022-09-03 14:41:08-- https://raw.githubusercontent.com/MDASH-shinshu/MDASH-T-DE/main/2/resources/sample.jpg raw.githubusercontent.com (raw.githubusercontent.com) をDNSに問いあわせています... 185.199.108.133, 185.199.110.133, 185.199.111.133, ... raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... 200 OK 長さ: 789195 (771K) [image/jpeg] `sample.jpg' に保存中 sample.jpg 100%[===================>] 770.70K 1.96MB/s 時間 0.4s 2022-09-03 14:41:10 (1.96 MB/s) - `sample.jpg' へ保存完了 [789195/789195] total 288 drwx------@ 1 wasaki staff 16384 9 3 14:41 . drwx------@ 1 wasaki staff 16384 9 3 14:00 .. -rwx------@ 1 wasaki staff 10244 9 3 14:01 .DS_Store drwx------@ 1 wasaki staff 16384 9 3 14:00 1 drwx------@ 1 wasaki staff 16384 9 3 14:39 2 drwx------@ 1 wasaki staff 16384 8 28 13:31 3 drwx------@ 1 wasaki staff 16384 9 3 14:37 CRF++-0.58 drwx------@ 1 wasaki staff 16384 8 10 11:53 _backups drwx------@ 1 wasaki staff 16384 9 3 14:03 _resources -rwx------@ 1 wasaki staff 790570 9 3 14:35 crfpp.tar.gz drwx------@ 1 wasaki staff 16384 8 10 11:48 ex0 -rwx------@ 1 wasaki staff 789195 9 3 14:41 sample.jpg
# ファイル名(sample.jpg)の部分は,自分がアップロードした画像ファイル名に変えてください
img = cv2.imread("./sample.jpg") # 画像の読み込み
print(img.shape) # 画像サイズ,色数
print(img.dtype) # 画素のビット幅
plt.figure(figsize=(10, 8))
im_rgb = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) # 色空間をBGR から RGB に変換する
plt.imshow(im_rgb) # matplot.imshowメソッドを用いて読み込んだ画像を表示
(1512, 2016, 3) uint8
<matplotlib.image.AxesImage at 0x1551b30a0>
plt.figure(figsize=(10, 8))
im_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # BGR を GRAYスケール に変換する(輝度信号 Y = 0.299 * R + 0.587 * G + 0.114 * B)
plt.imshow(im_gray, cmap="gray") # matplot.imshowメソッドを用いて読み込んだ画像を表示(グレースケール表示)
<matplotlib.image.AxesImage at 0x191518340>
## HSV色座標系に変換してヒストグラムを表示する関数 (引用:udemy | 【Pythonで学ぶ】OpenCVでの画像処理入門)
def get_hsv_report(rgb_image, plot_show = False, statistics_show=False):
"""HSV色座標系に変換してヒストグラムを表示する。さらにパーセントタイルを利用して全体的に明るい、暗いなどを検出する
Args:
rgb_image(obj): rgbイメージ画像
plot_show(bool): hsv票色系のグラフをプロットするか
statistics_show(bool):標準偏差を出力するか(処理が重い)
Returns:
hsv票色系のパーセントタイルの値
"""
hsv = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2HSV) # hsv票色系に変換
h,s,v = cv2.split(hsv) # 各成分に分割
def get_percentile_list(k, datas):
#ここでは、パーセントタイルを 5,50,95パーセントとする。
#パーセントタイルを利用して、輝度より明るい、暗い画像を判定などに利用する
percentile = [5,50,95] #パーセントタイルの設定。ここは、必要に応じて変更する
out_datas = {}
for i in percentile:
value = np.percentile(np.array(datas), i)
s = k + "_"+str(i)
out_datas[s] = value
return out_datas
out_dict = {}
plt.figure(figsize=(10, 8))
#色相
if(plot_show == True):
plt.hist(h.ravel(),256,[0,256], color="red", alpha=0.7, histtype="step", label="Hue")
data = get_percentile_list("h_per",h.ravel())
out_dict.update(data)
if(statistics_show == True):
out_dict['h_pstdev'] = statistics.pstdev(h.ravel()/255)
#彩度
if(plot_show == True):
plt.hist(s.ravel(),256,[0,256], color="green", alpha=0.7, histtype="step", label="Saturation")
data = get_percentile_list("s_per",h.ravel())
out_dict.update(data)
if(statistics_show == True):
out_dict['s_pstdev'] = statistics.pstdev(s.ravel()/255)
#輝度
if(plot_show == True):
plt.hist(v.ravel(),256,[0,256], color="blue", alpha=0.7, histtype="step",label="Value")
plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=0, fontsize=10)
plt.show()
data = get_percentile_list("v_per",v.ravel())
out_dict.update(data)
if(statistics_show == True):
out_dict['v_pstdev'] = statistics.pstdev(v.ravel()/255)
return out_dict
hsv_report = get_hsv_report(img,True) # オリジナル画像 img に対して 色相(Hue), 彩度(Saturation・Chroma)、明度・輝度(Value・Brightness)のヒストグラムを表示
#print(hsv_report)
#輝度は青色の Value で,元画像が「暗すぎる」「明るすぎる」の傾向が解る.
## 画像全体の明度を変更(明るめ,暗め)するため,ガンマ補正する関数 (引用:udemy | 【Pythonで学ぶ】OpenCVでの画像処理入門)
def gamma_correction(image,gamma):
"""ガンマ補正を利用して、画像を明るくしたり暗くしたりする
Args:
image(obj): イメージ画像
gamma(float): ガンマ値 0〜1までは、暗くする、1以上は明るくする
Returns:
ガンマ補正後のイメージ画像
"""
# 整数型で2次元配列を作成[256,1]
lookup_table = np.zeros((256, 1), dtype = 'uint8')
for loop in range(256):
# γテーブルを作成
lookup_table[loop][0] = 255 * pow(float(loop)/255, 1.0/gamma)
# lookup tableを用いてガンマ変換
image_gamma = cv2.LUT(image, lookup_table)
return image_gamma
plt.figure(figsize=(10, 8))
#ガンマ補正を利用して明るくする
im_gamma = gamma_correction(im_gray, 1.6) # ガンマ補正 1.6 に設定(調整してみよ)
plt.imshow(im_gamma, cmap="gray")
<matplotlib.image.AxesImage at 0x191b92980>
# 処理後の画像結果をカレントディレクトリに出力
cv2.imwrite("output.jpg", im_gamma)
True
ここでは音声データ解析の一歩として,Pythonと音声処理ライブラリ libROSAを使って,音声データの読み込み,再生,音声波形のグラフを表示,スペクトログラムへの変換(STFT),逆STFTでスペクトログラムから音声を復元を体験してみる.
以下では,基本的に Google colab.環境下でのPythonとlibROSAライブラリを前提として実装されている. また,サンプル画像のダウンロードには wgetコマンドを使用しているが,これはGoogle colab. あるいは Linux環境などで標準的に入っているが,Windows版ローカル実行の jupyter notebookでは別途インストールと設定作業が必要なので注意されたい.
入力音声データは,各自が手持ちにあるフリーの音声データや録音データなど,任意のデータをソースコードと同じフォルダ(Google colab.の場合は,各セッションに割当てられる一時作業領域)に保存してあるものとして説明する.
!pip install tqdm # 進捗状況を可視化するプログレスバー のライブラリ
!pip install librosa # 音声処理統合ライブラリ
!pip install matplotlib
!pip install pandas
Requirement already satisfied: tqdm in /usr/local/lib/python3.10/site-packages (4.64.0) Requirement already satisfied: librosa in /usr/local/lib/python3.10/site-packages (0.9.2) Requirement already satisfied: decorator>=4.0.10 in /usr/local/lib/python3.10/site-packages (from librosa) (5.1.1) Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/site-packages (from librosa) (21.3) Requirement already satisfied: joblib>=0.14 in /usr/local/lib/python3.10/site-packages (from librosa) (1.1.0) Requirement already satisfied: scikit-learn>=0.19.1 in /usr/local/lib/python3.10/site-packages (from librosa) (1.1.2) Requirement already satisfied: audioread>=2.1.9 in /usr/local/lib/python3.10/site-packages (from librosa) (3.0.0) Requirement already satisfied: scipy>=1.2.0 in /usr/local/lib/python3.10/site-packages (from librosa) (1.9.1) Requirement already satisfied: pooch>=1.0 in /usr/local/lib/python3.10/site-packages (from librosa) (1.6.0) Requirement already satisfied: resampy>=0.2.2 in /usr/local/lib/python3.10/site-packages (from librosa) (0.4.0) Requirement already satisfied: numpy>=1.17.0 in /usr/local/lib/python3.10/site-packages (from librosa) (1.23.2) Requirement already satisfied: numba>=0.45.1 in /usr/local/lib/python3.10/site-packages (from librosa) (0.56.2) Requirement already satisfied: soundfile>=0.10.2 in /usr/local/lib/python3.10/site-packages (from librosa) (0.10.3.post1) Requirement already satisfied: setuptools<60 in /usr/local/lib/python3.10/site-packages (from numba>=0.45.1->librosa) (59.8.0) Requirement already satisfied: llvmlite<0.40,>=0.39.0dev0 in /usr/local/lib/python3.10/site-packages (from numba>=0.45.1->librosa) (0.39.1) Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/local/lib/python3.10/site-packages (from packaging>=20.0->librosa) (3.0.9) Requirement already satisfied: requests>=2.19.0 in /usr/local/lib/python3.10/site-packages (from pooch>=1.0->librosa) (2.28.1) Requirement already satisfied: appdirs>=1.3.0 in /usr/local/lib/python3.10/site-packages (from pooch>=1.0->librosa) (1.4.4) Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/site-packages (from scikit-learn>=0.19.1->librosa) (3.1.0) Requirement already satisfied: cffi>=1.0 in /usr/local/lib/python3.10/site-packages (from soundfile>=0.10.2->librosa) (1.15.1) Requirement already satisfied: pycparser in /usr/local/lib/python3.10/site-packages (from cffi>=1.0->soundfile>=0.10.2->librosa) (2.21) Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.10/site-packages (from requests>=2.19.0->pooch>=1.0->librosa) (1.26.12) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/site-packages (from requests>=2.19.0->pooch>=1.0->librosa) (3.3) Requirement already satisfied: charset-normalizer<3,>=2 in /usr/local/lib/python3.10/site-packages (from requests>=2.19.0->pooch>=1.0->librosa) (2.1.1) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/site-packages (from requests>=2.19.0->pooch>=1.0->librosa) (2022.6.15) Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/site-packages (3.5.3) Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/site-packages (from matplotlib) (4.37.1) Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/site-packages (from matplotlib) (0.11.0) Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/site-packages (from matplotlib) (21.3) Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/site-packages (from matplotlib) (9.2.0) Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/site-packages (from matplotlib) (1.4.4) Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/site-packages (from matplotlib) (2.8.2) Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.10/site-packages (from matplotlib) (1.23.2) Requirement already satisfied: pyparsing>=2.2.1 in /usr/local/lib/python3.10/site-packages (from matplotlib) (3.0.9) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0) Requirement already satisfied: pandas in /usr/local/lib/python3.10/site-packages (1.4.4) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/site-packages (from pandas) (2022.2.1) Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/site-packages (from pandas) (2.8.2) Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/site-packages (from pandas) (1.23.2) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/site-packages (from python-dateutil>=2.8.1->pandas) (1.16.0)
import os
from tqdm import tqdm # tqdmライブラリをインポート
import numpy as np
import matplotlib.pyplot as plt
import IPython.display
from IPython.display import display
import pandas as pd
import librosa
import librosa.display
import librosa.util
# pyplot: matplotlibパッケージ内のモジュール.欲しいプロットを作るために暗黙的かつ自動的に図形や軸を作成するインターフェース
# 以下で,pyplotのデフォルト値を設定しておく
plt.rcParams.update({
'font.size' : 10
,'font.family' : 'Meiryo' if os.name == 'nt' else 'DejaVu Sans' # Google colab.では日本語フォントがインストールされていない
,'figure.figsize' : [10.0, 5.0]
,'figure.dpi' : 300
,'savefig.dpi' : 300
,'figure.titlesize' : 'large'
,'legend.fontsize' : 'small'
,'axes.labelsize' : 'medium'
,'xtick.labelsize' : 'small'
,'ytick.labelsize' : 'small'
})
# 多次元配列のデータ構造 ndarray の表示設定
np.set_printoptions(threshold=0) # 可能ならndarrayを省略して表示
np.set_printoptions(edgeitems=1) # 省略時に1つの要素だけ表示
# MP3音声データを カレントディレクトリ直下のフォルダ(一時作業領域)へダウンロードする.
# サンプル音声素材 tam-x05.mp3 クリスマス風 ゆっくり目 (Copyright© TAM Music Factory All Rights Reserved.)
!wget -nc https://raw.githubusercontent.com/MDASH-shinshu/MDASH-T-DE/main/2/resources/tam-x05.mp3
# wgetしなくても,Google colab.の左メニュー [ファイル] アイコンをクリックして,ブラウザへファイルをドラッグ&ドロップしても可
# 音声データがダウンロード・配置できたことを確認する
!ls -al ./
--2022-09-03 14:43:04-- https://raw.githubusercontent.com/MDASH-shinshu/MDASH-T-DE/main/2/resources/tam-x05.mp3 raw.githubusercontent.com (raw.githubusercontent.com) をDNSに問いあわせています... 185.199.110.133, 185.199.111.133, 185.199.109.133, ... raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... 200 OK 長さ: 870416 (850K) [audio/mpeg] `tam-x05.mp3' に保存中 tam-x05.mp3 100%[===================>] 850.02K 2.13MB/s 時間 0.4s 2022-09-03 14:43:07 (2.13 MB/s) - `tam-x05.mp3' へ保存完了 [870416/870416] total 288 drwx------@ 1 wasaki staff 16384 9 3 14:43 . drwx------@ 1 wasaki staff 16384 9 3 14:00 .. -rwx------@ 1 wasaki staff 10244 9 3 14:01 .DS_Store drwx------@ 1 wasaki staff 16384 9 3 14:00 1 drwx------@ 1 wasaki staff 16384 9 3 14:41 2 drwx------@ 1 wasaki staff 16384 8 28 13:31 3 drwx------@ 1 wasaki staff 16384 9 3 14:37 CRF++-0.58 drwx------@ 1 wasaki staff 16384 8 10 11:53 _backups drwx------@ 1 wasaki staff 16384 9 3 14:03 _resources -rwx------@ 1 wasaki staff 790570 9 3 14:35 crfpp.tar.gz drwx------@ 1 wasaki staff 16384 8 10 11:48 ex0 -rwx------@ 1 wasaki staff 1147470 9 3 14:42 output.jpg -rwx------@ 1 wasaki staff 789195 9 3 14:41 sample.jpg -rwx------@ 1 wasaki staff 870416 9 3 14:43 tam-x05.mp3
# 上記でダウンロードした音声ファイルを設定
audio_path = 'tam-x05.mp3'; print(audio_path)
# あるいは,librosaに標準で入っているサンプル音声ファイルを読込み (https://librosa.org/librosa/master/generated/librosa.core.load.html)
# サンプル音声素材 Kevin_MacLeod_-_Vibe_Ace.hq.ogg ポップアップ 速めのテンポ
#audio_path = librosa.util.example_audio_file(); print(audio_path)
# ターゲットとするサンプリング周波数で読み込む
y, sr = librosa.load(audio_path) # サンプリング周波数 22.05kHzで読み込む (default)
# y, sr = librosa.load(audio_path, sr=None) # オリジナルのサンプリング周波数で読み込む
# y, sr = librosa.load(audio_path, sr=4096) # 4kHzで re-samplingして読み込む
# 読み込み結果の表示
print([type(y), y.shape], [type(sr), sr]) # sr はSampling Rate(サンプリング周波数)の略
tam-x05.mp3
/usr/local/lib/python3.10/site-packages/librosa/util/decorators.py:88: UserWarning: PySoundFile failed. Trying audioread instead. return f(*args, **kwargs)
[<class 'numpy.ndarray'>, (1597760,)] [<class 'int'>, 22050]
# IPython経由でブラウザの音声プレーヤを呼び出して読み込んだデータ列 y を サンプリングレート sr で再生する
# IPython.display.display() ドキュメント https://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html#IPython.display.display
display(IPython.display.Audio(y, rate=sr))
time = np.arange(0,len(y)) / sr # 時間 = yのデータ数 / サンプリング周波数sr
plt.plot(time, y) # xにtime、yにyとしてプロット
plt.xlabel("Time[sec]") # x軸とy軸にラベルを設定(x軸は時間、y軸は振幅)
plt.ylabel("Sound Amplitude")
plt.show() # mathplot経由でx-yグラフを表示
音声信号に対して,時間⇒周波数空間変換,具体的には「短時間フーリエ変換(Short-time Fourier transform : STFT)」を行うことで,スペクトログラムが得られる. スペクトログラムとは,音声を周波数スペクトルの時間経過で示したものである.
D = librosa.stft(y) # Short-time Fourier transform : STFT (default FFTウィンドウ幅 n_fft=2048, 移動幅 hop_length=n_fft/4)
S, phase = librosa.magphase(D) # FFT変換後は複素数となっているので,それを 強度S と 位相phase へ分離
Sdb = librosa.amplitude_to_db(S) # 強度S を デジベル(音圧)Sdb へ変換
librosa.display.specshow(Sdb, sr=sr, x_axis='time', y_axis='log') # スペクトログラムを表示(周波数yは対数スケール)
<matplotlib.collections.QuadMesh at 0x1929912a0>
tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr) # BPM(音楽の場合,1分間あたりの拍子の数)
print(tempo)
86.1328125
# ピッチシフト : 1オクターブ上にしてみる
label = "1 octave down"
eps = 1.0
n_fft = 2048
y_shift = librosa.effects.pitch_shift(y, sr, n_steps=+12) # 1オクターブ上下: n_steps=±12, 完全5度上(半音7個分): n_steps=7 など
##plt.plot(np.log(np.abs(np.fft.fft(y_shift))**2+eps)[0:n_fft//2+1], label=label, alpha=0.5)
display(IPython.display.Audio(y_shift, rate=sr))
/var/folders/zk/s10ljgvj0ql0g5f0b61h5dm80000gn/T/ipykernel_16403/3886268396.py:5: FutureWarning: Pass sr=22050 as keyword args. From version 0.10 passing these as positional arguments will result in an error y_shift = librosa.effects.pitch_shift(y, sr, n_steps=+12) # 1オクターブ上下: n_steps=±12, 完全5度上(半音7個分): n_steps=7 など
# 1/2倍にタイムストレッチ
y_slow = librosa.effects.time_stretch(y, 1./2)
##plt.plot(np.log(np.abs(np.fft.fft(y_slow))**2+eps)[0:n_fft//2+1], label=label, alpha=0.5)
display(IPython.display.Audio(y_slow, rate=sr))
/var/folders/zk/s10ljgvj0ql0g5f0b61h5dm80000gn/T/ipykernel_16403/2279594002.py:2: FutureWarning: Pass rate=0.5 as keyword args. From version 0.10 passing these as positional arguments will result in an error y_slow = librosa.effects.time_stretch(y, 1./2)